package com.inspire.ijc.agent;

import com.inspire.ijc.agent.utils.FileUtils;
import com.inspire.ijc.agent.utils.WildcardMatcher;
import com.inspire.ijc.boot.LoadState;

import java.io.File;
import java.lang.instrument.Instrumentation;
import java.net.URISyntaxException;
import java.security.CodeSource;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;

/**
 * @author ：inspire
 * @date ：Created in 2023/4/13 10:01
 * @description：Agent启动类
 * @modified By：inspire
 * @version:
 */
public class IjcAgent {
    private static final LoadState STATE = new LoadState();
    private static final Pattern OPTION_SPLIT = Pattern.compile(",");

    private static final String excludes = "excludes";
    private static final String includes = "includes";
    private static final String moduleName = "moudleName";
    private static final String filterMethod="filterMethod";
    private static final String timeout="timeout";

    public static WildcardMatcher wmIncludes =null;
    public static WildcardMatcher wmExcludes =null;
    public static File IJC_OUTPUT_DIR =null;
    public static String IJCBOOTTIME=null;
    public static String moudleName_V=null;
    public static List<String> filterMethod_V=new ArrayList<String>();
    public static int timeout_V=0;

    private static final Collection<String> VALID_OPTIONS = Arrays.asList(excludes, includes,moduleName,filterMethod,timeout);

    public static void premain(String agentOps, Instrumentation inst) {

        startProcessing(agentOps, inst);
    }

    public static void startProcessing(String agentOps, Instrumentation inst) {
        final boolean success = STATE.start();

        if (!success) {
            System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()) + " ijc already started. skipping ijc agent loading.");
            return;
        }
        AgentOptions(agentOps);

        if(wmIncludes ==null){
            System.out.println("To use ijc, you must configure includes!\n      eg:com.openai.*;com.meta.*");
            return;
        }
        CodeSource codeSource = IjcAgent.class.getProtectionDomain().getCodeSource();
        if(codeSource!=null){
            try {
                String specificPart = codeSource.getLocation().toURI().getSchemeSpecificPart();
                specificPart=specificPart.substring(0,specificPart.lastIndexOf("ijc-1.0.jar"));
                if(moudleName_V!=null){
                    specificPart+=moudleName_V+File.separator;
                    IJC_OUTPUT_DIR =new File(specificPart);
                    if(!IJC_OUTPUT_DIR.exists()){
                        IJC_OUTPUT_DIR.mkdir();
                    }
                }else{
                    IJC_OUTPUT_DIR =new File(specificPart);
                }

            } catch (URISyntaxException e) {
                e.printStackTrace();
            }
        }
        if(IJC_OUTPUT_DIR ==null){
            System.out.println("ijc cannot find the jar package address and cannot output content to a file");
            return;
        }
        IJCBOOTTIME= FileUtils.getCurrentDateStr(FileUtils.DATE_PATTERN_NOMARK2);
        inst.addTransformer(new InsertCode());
    }

    private static void AgentOptions(String agentOps) {
        if (agentOps != null && agentOps.length() > 0) {
            for (final String entry : OPTION_SPLIT.split(agentOps)) {
                final int pos = entry.indexOf('=');
                if (pos == -1) {
                    throw new IllegalArgumentException("Invalid agent option syntax " + agentOps);
                }
                final String key = entry.substring(0, pos);

                if (!VALID_OPTIONS.contains(key)) {
                    throw new IllegalArgumentException("Unknown agent option " + key);
                }

                final String value = entry.substring(pos + 1);
                setOption(key, value);
            }
        }
    }

    private static void setOption(String key, String value) {
        if (!value.isEmpty() && !"".equals(value)) {
            if (key.equalsIgnoreCase(includes)) {
                value=value.replace(".","/");
                wmIncludes =new WildcardMatcher(value);
            }else if (key.equalsIgnoreCase(excludes)) {
                value=value.replace(".","/");
                wmExcludes =new WildcardMatcher(value);
            }else if(key.equals(moduleName)){
                moudleName_V=value;
            }else if(key.equals(filterMethod)){
                String[] filterMethods = value.split(";");
                for(String method:filterMethods){
                    filterMethod_V.add(method);
                }
            }else if(key.equals(timeout)){
                timeout_V=Integer.parseInt(value);
            }
        }

    }

    public static String isWindows() {
        String sysName = System.getProperty("os.name").toLowerCase();
        if (sysName.contains("win")) {
            return ";";
        } else {
            return ":";
        }
    }
}
